%{
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string>
#include <vector>
#include "cfa.h"
//#include "lex.yy.cc"
using namespace std;
int yylex(void);
void yyerror(char*);
bool ParseError=false;
double vbltable[26]; 
int expInt(int, int);
string I2S(int);

extern char* yytext;
Prog *prog;
//Prog *prog1;
vector<VarExpr*> formalpara;
VarExpr *vp;
vector<Expr*> actualpara;
vector<Prog*> fun;
int to_line = 0;
int no_assign = 0;
int no_if = 0; 
int no_while = 0;
int no_assert = 0;
//Node* n;
//InequProp *ip;
//function
//extern exptype* AddBinExpr(exptype *S1, exptype *S2, BinExpr* b, OPType op, vector<Expr*>* Exprs);
%}

%union {
    double dval;
    int vblno;
    char str[256];
    bool btype;
    struct exptype *EF;
    struct Expr *Ex;
    struct Prop *Pp;
    struct Stmt *St;
    struct AssignStmt *AS;
    struct IfStmt *IS;
    struct WhileStmt *WS;
    struct AssertStmt *ASS;
    struct GotoStmt *GS;
    struct LabelStmt *LS;
    struct CallFunStmt *CFS;
    struct Prog *Pg;
}

%token RETURN GOTO CONTINUE BREAK
%token THEN EOLINE
%token IF EQUAL ASSIGN LL GL DOT LPART RPART LMPART RMPART LBPART RBPART COMMA COLON NONEQUAL
%token ELSE SKIP CALL
%token WHILE
%token ASSERT;
%token <vblno> INTEGER;
%token <str> STRING;
%token <btype>TRUE;
%token <btype>FALSE;
%left NOT;
%left AND;
%left OR;
%left PLUS MINUS;
%left MULTI DIVID;

%type <Ex> expression;
%type <St> statement;
%type <AS> assign_stmt;
%type <IS> if_else_stmt;
%type <IS> if_stmt;
%type <WS> while_stmt;
%type <ASS> assert_stmt;
%type <CFS> callfunction_stmt;
%type <Pp> compare_exp;
%type <Pg> program;
%start function
%%
function: STRING LPART formal_para RPART LBPART program RBPART		{
                                 fun.clear();
                                 strcpy($6->name, $1);
				 $6->formal_para = formalpara;
				 fun.push_back($6);
        }
        | function STRING LPART formal_para RPART LBPART program RBPART	{
                                 strcpy($7->name, $2);
				 $7->formal_para = formalpara;
				 fun.push_back($7);
        }
        ;
formal_para: STRING			{ 
				 vp = new VarExpr;
			  	 strcpy(((VarExpr*)vp)->x, $1);
				 formalpara.clear();
				 formalpara.push_back(vp);
					}
        |    formal_para COMMA STRING	{
				 vp = new VarExpr;
			  	 strcpy(((VarExpr*)vp)->x, $3);
				 formalpara.push_back(vp);
					}
        |
        ;
program: statement             { 
                                 $$ = new Prog;
                                 $$->p.push_back($1);
                                 //prog1 = $$;
                               }
       | program statement     {
                                 $$->p.push_back($2);
                                 //prog1 = $$;
                               }
       ;
statement:assign_stmt          { to_line = to_line + 1; 
                                 no_assign = no_assign + 1;
                                 $$ = new AssignStmt;
                                 $$ = $1;
                               }
         | if_else_stmt        { to_line = to_line + 1;
                                 no_if = no_if + 1;
                                 $$ = new IfStmt;
                                 $$ = $1;
                               }
         | if_stmt	       {
         		         to_line = to_line + 1;
                                 $$ = new IfStmt;
                                 $$ = $1;
                               }
         | while_stmt          { to_line = to_line + 1;
                                 no_while = no_while + 1;
                                 $$ = new WhileStmt;
                                 $$ = $1;
                               }
         | assert_stmt         { to_line = to_line + 1;
                                 no_assert = no_assert + 1;
                                 $$ = new AssertStmt;
                                 $$ = $1;
                               }
         | callfunction_stmt   {
				 to_line = to_line + 1;
                                 $$ = new CallFunStmt;
                                 $$ = $1;
                               }
         | STRING COLON        {
         		         to_line = to_line + 1;
                                 $$ = new LabelStmt;
                                 strcpy(((LabelStmt*)$$)->L, $1);
                               }
         | GOTO STRING	       {
         		         to_line = to_line + 1;
				 $$ = new GotoStmt;
                                 ((GotoStmt*)$$)->type = 1;
                                 strcpy(((GotoStmt*)$$)->L, $2);
			       }
	 | RETURN expression   {
         		         to_line = to_line + 1;
				 $$ = new GotoStmt;
                                 ((GotoStmt*)$$)->type = 4;
                                 strcpy(((GotoStmt*)$$)->L, "return");
                                 ((GotoStmt*)$$)->e = $2;
			       }
	 | CONTINUE	       {
         		         to_line = to_line + 1;
				 $$ = new GotoStmt;
                                 ((GotoStmt*)$$)->type = 2;
                                 strcpy(((GotoStmt*)$$)->L, "continue");
 			       }
	 | BREAK	       {
         		         to_line = to_line + 1;
				 $$ = new GotoStmt;
                                 ((GotoStmt*)$$)->type = 3;
                                 strcpy(((GotoStmt*)$$)->L, "break");
			       }
         | SKIP		       { 
         		       }
         ;
assign_stmt: expression ASSIGN expression       {
                                                  $$ = new AssignStmt;
                                                  $$->e1 = $1;
                                                  $$->e2 = $3;
           }
           ;
expression: expression PLUS expression          { 
                                                  $$ = new BinExpr;
                                                  ((BinExpr*)$$)->e1 = $1;
                                                  ((BinExpr*)$$)->e2 = $3;
                                                  ((BinExpr*)$$)->op = Plus;
          }
          | expression MINUS expression         {
                                                  $$ = new BinExpr;
                                                  ((BinExpr*)$$)->e1 = (VarExpr*)$1;
                                                  ((BinExpr*)$$)->e2 = (VarExpr*)$3;
                                                  ((BinExpr*)$$)->op = Minus;
          }
          | expression MULTI expression         {
                                                  $$ = new BinExpr;
                                                  ((BinExpr*)$$)->e1 = (VarExpr*)$1;
                                                  ((BinExpr*)$$)->e2 = (VarExpr*)$3;
                                                  ((BinExpr*)$$)->op = Multi;
          }
          | expression DIVID expression         {
                                                  $$ = new BinExpr;
                                                  ((BinExpr*)$$)->e1 = (VarExpr*)$1;
                                                  ((BinExpr*)$$)->e2 = (VarExpr*)$3;
                                                  ((BinExpr*)$$)->op = Divide;
          }
          | STRING LMPART expression RMPART    {
                                                  $$ = new SelectExpr;
						  ((SelectExpr*)$$)->a = new VarExpr;
                                                  strcpy(((VarExpr*)(((SelectExpr*)$$)->a))->x, $1);
                                                  ((SelectExpr*)$$)->i = $3;
          }
          | STRING                              { 
                                                  $$ = new VarExpr;
                                                  strcpy(((VarExpr*)$$)->x, $1);
          }
          | INTEGER                             { 
                                                  $$ = new IntExpr;
                                                  ((IntExpr*)$$)->i = $1;
          }
          ;
if_stmt: IF LPART compare_exp RPART statement 			{
                                                                  $$ = new IfStmt;
                                                                  ((IfStmt*)$$)->p = $3;
                                                                  ((IfStmt*)$$)->p1 = new Prog;
								  (((IfStmt*)$$)->p1)->p.push_back($5);
                                                                  //printf("size = %d\n", ($$->p1)->p.size());
                                                                  $$->p2 = NULL;
								}
	 | IF LPART compare_exp RPART LBPART program RBPART 	{
                                                                  $$ = new IfStmt;
                                                                  ((IfStmt*)$$)->p = $3;
                                                                  $$->p1 = $6;
                                                                  $$->p2 = NULL;
								}
	 ;
if_else_stmt: IF LPART compare_exp RPART LBPART program RBPART ELSE LBPART program RBPART {
                                                                                            $$ = new IfStmt;
                                                                                            ((IfStmt*)$$)->p = $3;
                                                                                            $$->p1 = $6;
                                                                                            $$->p2 = $10;
            }
            ;
while_stmt: WHILE LPART compare_exp RPART LBPART program RBPART                           { 
                                                                                            $$ = new WhileStmt;
                                                                                            ((WhileStmt*)$$)->p = $3;
                                                                                            ((WhileStmt*)$$)->p1 = $6;
          }
          ;
assert_stmt: ASSERT LPART compare_exp RPART                                               {
                                                                                            $$ = new AssertStmt;
                                                                                            ((AssertStmt*)$$)->p = $3;
           }
           ;
compare_exp: expression EQUAL expression                                                  {
                                                                                            $$ = new InequProp;
                                                                                            ((InequProp*)$$)->e1 = $1;
                                                                                            ((InequProp*)$$)->e2 = $3;
                                                                                            ((InequProp*)$$)->p = Eq;
           }
           | expression NONEQUAL expression                                                 {
                                                                                            $$ = new NegProp;
                                                                                            ((NegProp*)$$)->p = new InequProp;
                                                                                            ((InequProp*)(((NegProp*)$$)->p))->e1 = $1;
                                                                                            ((InequProp*)(((NegProp*)$$)->p))->e2 = $3;
                                                                                            ((InequProp*)(((NegProp*)$$)->p))->p = Eq;
           }
           | expression LL expression                                                     {
                                                                                            $$ = new InequProp;
                                                                                            ((InequProp*)$$)->e1 = $1;
                                                                                            ((InequProp*)$$)->e2 = $3;
                                                                                            ((InequProp*)$$)->p = LT;
           }
           | expression GL expression                                                     {
                                                                                            $$ = new InequProp;
                                                                                            ((InequProp*)$$)->e1 = $1;
                                                                                            ((InequProp*)$$)->e2 = $3;
                                                                                            ((InequProp*)$$)->p = GT;
           }
           | TRUE                                                                         {
                                                                                            $$ = new BoolProp;
                                                                                            ((BoolProp*)$$)->b = true;
           }
           | FALSE                                                                        {
                                                                                            $$ = new BoolProp;
                                                                                            ((BoolProp*)$$)->b = false;
           }
           | NOT compare_exp                                                              {
                                                                                            $$ = new NegProp;
                                                                                            ((NegProp*)$$)->p = $2;
           }
           | compare_exp AND compare_exp                                                  {
                                                                                            $$ = new BinProp;
                                                                                            ((BinProp*)$$)->p1 = $1;
                                                                                            ((BinProp*)$$)->p2 = $3;
                                                                                            ((BinProp*)$$)->l = And;
           }
           | compare_exp OR compare_exp                                                   {
                                                                                            $$ = new BinProp;
                                                                                            ((BinProp*)$$)->p1 = $1;
                                                                                            ((BinProp*)$$)->p2 = $3;
                                                                                            ((BinProp*)$$)->l = Or;
           }
           ;
callfunction_stmt: STRING LPART parameter RPART        {
							 $$ = new CallFunStmt;
							 strcpy(((CallFunStmt*)$$)->fun, $1);
                 					 ((CallFunStmt*)$$)->para = actualpara;
							 ((CallFunStmt*)$$)->Ret = NULL;
                                                       }
	   | expression ASSIGN STRING LPART parameter RPART{
							 $$ = new CallFunStmt;
							 strcpy(((CallFunStmt*)$$)->fun, $3);
                 					 ((CallFunStmt*)$$)->para = actualpara;
                 					 ((CallFunStmt*)$$)->Ret = $1;
                                                         //printf("first!!! size = %d\n", ((CallFunStmt*)$$)->para.size());
							   }
           ;
parameter: expression                               	{
							 actualpara.clear();
							 actualpara.push_back($1);
                                                        }
           | parameter COMMA expression                 {
							 actualpara.push_back($3);
                                                        }
           ;
%%

void yyerror(char *s){
    cout << s << endl;
    cout << "\t Parsing error in line at " << yytext << endl;
    ParseError=true;
}


